home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Nebula 1
/
Nebula One.iso
/
Financial
/
Stopwatch2.3
/
Source
/
SortList.m
< prev
next >
Wrap
Text File
|
1995-06-12
|
12KB
|
507 lines
/*
* A subclass of List with a built-in sorting capability. The ShellSort
* code from the example program "SortingInAction" is used here, modified
* slightly to work closely with the List object's data representation, i.e.
* the array of objects pointed to by 'dataPtr'.
*
* The user of this class must set the SortingList's delegate to an object
* which responds to the 'lessThan:(int)position1 :(int)position2' method,
* which returns YES if object1 should be deemed to be "less than" object2
* in a way that makes sense to the caller.
*
* Written by Rich Plevin, 24 Jun 92.
*
* This code may be freely used, copied and modified. The author disclaims
* any warranty of any kind, expressed or implied, as to its fitness for any
* particular use.
*
* 8/18/92 - More general structure implemented (can set sort, comparison, and
* value methods. (value method returns the value for sorted key). added
* insertion method which inserts object in order. added sort flag and
* autosort feature.
*
* 8/19/92 - Added mergeList method and overrode indexOf to use binary search
*
* For legal stuff see the file COPYRIGHT
*/
#include "SortList.h"
#define STRIDE_FACTOR 3 // good value for stride factor is not well-understood
// 3 is a fairly good choice (Sedgewick)
@implementation SortList
- _updateChain
{
if (!delegate) {
compObject = self;
if ([self respondsTo:compareMethod]) {
compMethod = compareMethod;
}
else {
compMethod = @selector(compare::);
}
}
else {
if ([delegate respondsTo:compareMethod]) {
compObject = delegate;
compMethod = compareMethod;
}
else if ([delegate respondsTo:@selector(compare::)]) {
compObject = delegate;
compMethod = @selector(compare::);
}
else if ([self respondsTo:compareMethod]) {
compObject = self;
compMethod = compareMethod;
}
else {
compObject = self;
compMethod = @selector(compare::);
}
}
return self;
}
- initCount:(unsigned int)numSlots
{
[super initCount:numSlots];
compareMethod = @selector(compare::);
compMethod = @selector(compare::);
sortMethod = @selector(shellsort);
compObject = self;
sorted = NO;
autoSort = NO;
return self;
}
- (unsigned int)indexOf:anObject
/* overridden to use binary search. assumes that object used for search and
* object in list return the same key value
*/
{
int index1 = 0, index2 = numElements - 1, mid, mark, val;
if (anObject == nil)
return NX_NOT_IN_LIST;
if (sorted == NO)
return [super indexOf:anObject];
mid = (index2 + index1)/2;
while (mid > index1) {
if (anObject == dataPtr[mid])
return mid;
if ((val = (int)[compObject perform:compMethod with:anObject
with:dataPtr[mid]]) < 0) {
index2 = mid;
}
else if (val == 0) {
mark = mid;
while (val == 0) {
if (anObject == dataPtr[mark]) {
return mark;
}
mark--;
val = (int)[compObject perform:compMethod with:anObject
with:dataPtr[mark]];
}
mark = mid + 1;
while (val == 0) {
if (anObject == dataPtr[mark]) {
return mark;
}
mark++;
val = (int)[compObject perform:compMethod with:anObject
with:dataPtr[mark]];
}
return NX_NOT_IN_LIST;
}
else {
index1 = mid;
}
mid = (index2 + index1)/2;
}
if (anObject == dataPtr[index1]) {
return index1;
}
if (anObject == dataPtr[index2]) {
return index2;
}
return NX_NOT_IN_LIST;
}
- addObject:anObject
{
if (anObject == nil) return nil;
[super addObject:anObject];
[self update];
return self;
}
- addObjectIfAbsent:anObject
{
if (anObject == nil) return nil;
[super addObjectIfAbsent:anObject];
[self update];
return self;
}
- insertObject:anObject at:(unsigned int)index
{
id returnObj;
if (returnObj = [super insertObject:anObject at:index])
[self update];
return returnObj;
}
- replaceObject:anObject with:newObject
{
id returnObj;
if (returnObj = [super replaceObject:anObject with:newObject])
[self update];
return returnObj;
}
- replaceObjectAt:(unsigned int)index with:newObject;
{
id returnObj;
if (returnObj = [super replaceObjectAt:index with:newObject])
[self update];
return returnObj;
}
- update
/* sorts list if autoSort is enabled, otherwise just sets the sorted flag
* to NO.
*/
{
if (autoSort)
[self sort];
else
sorted = NO;
return self;
}
- (BOOL)isSorted
{
return sorted;
}
- setAutoSort:(BOOL)sortFlag
{
autoSort = sortFlag;
return self;
}
- (BOOL)doesAutoSort
{
return autoSort;
}
- setDelegate:obj
{
delegate = obj;
[self _updateChain];
return self;
}
- delegate
{
return delegate;
}
- useSortMethod:(SEL)method
/* sort method can be defined in a subclass or in a delegate(??). when -sort
* is called, the SortList first checks the delegate then itself. If neither
* can respond, then a default(shellsort) is used.
*/
{
sortMethod = method;
return self;
}
- (SEL)sortMethod
{
return sortMethod;
}
- useComparisonMethod:(SEL)method
/* sets the method used to compare the objects. Overrides SortList's and
* delegate's -compare:: methods. Note that the compare method can, and in
* most cases should, use the set valueMethod, if any were set.
*/
{
compareMethod = method;
[self _updateChain];
return self;
}
- (SEL)comparisonMethod
{
return compareMethod;
}
- useValueMethod:(SEL)method
/* sets method used to get value for sort key from objects. */
{
valueMethod = method;
return self;
}
- (SEL)valueMethod
{
return valueMethod;
}
- sort
/* the chain goes something like this: delegate:sortMethod, self:sortMethod,
* self:shellsort (default).
*/
{
if (!delegate) {
if ([self respondsTo:sortMethod])
[self perform:sortMethod with:self];
else [self shellsort];
}
else {
if ([delegate respondsTo:sortMethod])
[delegate perform:sortMethod with:self];
else if ([self respondsTo:sortMethod]) {
[self perform:sortMethod with:self];
}
else [self shellsort];
}
sorted = YES;
return self;
}
- insertObject:anObject
/* Inserts object into list in sorted order. if the list is not sorted, the
* list sorts itself regardless of whether autosort is enabled.
*/
{
int index1 = 0, index2 = numElements - 1, mid, val;
if (!sorted)
[self sort];
if (anObject == nil)
return nil;
mid = (index2 + index1)/2;
if ([self count] == 0) {
[self insertObject:anObject at:0];
return self;
}
while (mid > index1) {
if (anObject == dataPtr[mid])
return self;
if ((val = (int)[compObject perform:compMethod with:anObject
with:dataPtr[mid]]) < 0) {
index2 = mid;
} else if (val == 0) {
[self insertObject:anObject at:mid + 1];
return self;
} else {
index1 = mid;
}
mid = (index2 + index1)/2;
} if ((val = (int)[compObject perform:compMethod with:anObject
with:dataPtr[index1]]) < 0) {
[self insertObject:anObject at:index1];
} else if ((val = (int)[compObject perform:compMethod with:anObject
with:dataPtr[index2]]) > 0) {
[self insertObject:anObject at:index2 + 1];
} else {
[self insertObject:anObject at:index1 + 1];
}
sorted = YES;
return self;
}
- mergeList:otherList
/* merges otherList into the list, the new list is sorted. */
{
int i, j, val, ocount;
BOOL otherSorted, isSortList;
if (![otherList isKindOf:[List class]]) {
return nil;
}
if ([otherList respondsTo:@selector(isSorted)] &&
[otherList respondsTo:@selector(sort)]) {
isSortList = YES;
otherSorted = [otherList isSorted];
}
else {
otherSorted = NO;
isSortList = NO;
}
ocount = [otherList count];
i = j = 0;
if (!otherSorted && sorted) {
for (i = 0; i < ocount; i++) {
[self insertObject:[otherList objectAt:i]];
}
}
else if (otherSorted && sorted) {
while (j < ocount) {
while ((val = (int)[compObject perform:compMethod
with:dataPtr[i]
with:[otherList objectAt:j]]) < 0) {
i++;
if (i >= numElements) {
i = numElements - 1;
}
}
[self insertObject:[otherList objectAt:j] at:i];
j++;
}
}
else if (!otherSorted && !sorted) {
[self appendList:otherList];
[self sort];
}
return self;
}
- (unsigned int)indexOfObjectWithKey:proxy
{
int index1 = 0, index2 = numElements - 1, mid, mark, val;
if (proxy == nil || [self count] == 0)
return NX_NOT_IN_LIST;
if (sorted == NO) {
[self sort];
}
// return [super indexOf:anObject];
mid = (index2 + index1)/2;
while (mid > index1) {
if ((val = (int)[compObject perform:compMethod with:proxy
with:dataPtr[mid]]) < 0) {
index2 = mid;
}
else if (val == 0) {
mark = mid;
while (val == 0) {
mark--;
val = (int)[compObject perform:compMethod with:proxy
with:dataPtr[mark]];
}
return mark + 1;
}
else {
index1 = mid;
}
mid = (index2 + index1)/2;
}
if (((int)[compObject perform:compMethod with:proxy
with:dataPtr[index1]]) == 0) {
return index1;
}
if (((int)[compObject perform:compMethod with:proxy
with:dataPtr[index2]]) == 0) {
return index2;
}
return NX_NOT_IN_LIST;
}
/*
* This code was extracted from the /NextDeveloper/Examples/SortingInAction program.
* The original author's comment:
*
* Because Shellsort is a variation on Insertion Sort, it has the same
* inconsistency that I noted in the InsertionSort class. Notice where I
* subtract a move to compensate for calling a swap for visual purposes.
*
* Author: Julie Zelenski, NeXT Developer Support
* You may freely copy, distribute and reuse the code in this example.
* NeXT disclaims any warranty of any kind, expressed or implied, as to
* its fitness for any particular use.
*
* [This sounds like there is unnecessary work happening in this routine, but I
* didn't dig into it enough to figure out what to change - rjp]
*
* Depending on what methods are set (and whether a delegate is set), the
* comparison method will vary. The chain of precedence is:
* delegate:comparisonMethod, delegate:compare::, self:comparisonMethod,
* self:compare::.
*/
- shellsort
{
int c, d, stride;
BOOL found;
stride = 1;
while (stride <= numElements)
stride = stride * STRIDE_FACTOR + 1;
while (stride > STRIDE_FACTOR - 1 ) {
/* loop to sort for each value of stride */
stride = stride / STRIDE_FACTOR;
for (c = stride; c < numElements; c++) {
found = NO;
d = c - stride;
while ( d >= 0 && !found ) {
/* move to left until correct place */
int target = d + stride ;
if ((int)[compObject perform:compMethod
with:dataPtr[target]
with:dataPtr[d]] < 0) {
id tmp = dataPtr[target]; /* swap the elements */
dataPtr[target] = dataPtr[d];
dataPtr[d] = tmp ;
d -= stride; /* jump by stride factor */
} else
found = YES;
}
}
}
return self;
}
- (int)compare:object1 :object2
{
if ([object1 respondsTo:valueMethod] &&
[object2 respondsTo:valueMethod]) {
return [object1 perform:valueMethod] -
[object2 perform:valueMethod];
}
else
return ((int)object1) - ((int)object2);
}
@end
@implementation SortingListDelegate
/*
* Dummy delegated method
*/
- (int)compare:object1 :object2
{
return 0;
}
- sort:aList
{
[aList shellsort];
return self;
}
@end